package game.worlds;

import game.data.GameOption;
import echo.math.Vector2;
import echo.Body;
import echo.Shape;
import game.event.RoleEvent;
import game.script.GameEffectScript;
import game.utils.ITimelineTools;
import game.data.FrameData;
import hx.geom.ColorTransform;
import hx.display.Box;
import openfl.filters.ColorMatrixFilter;
import hx.display.BlendMode;
import hx.geom.Point;
import hx.display.DisplayObject;
import openfl.geom.Rectangle;
import openfl.display.Bitmap;
import hx.display.CustomDisplayObject;
import hx.assets.XmlAtlas;
import hx.events.Event;
import game.data.EffectData;
import hx.display.MovieClip;
import game.utils.IAttackDisplayTools;

/**
 * 游戏特效渲染，该显示对象为了还原幻想纹章2原有的改色功能，使用了自定义渲染模式，该模式会导致特效永远都会占用1drawcall，性能没有批处理的好，后续新编辑器中尽可能减少使用此类特效
 */
class GameEffect extends Box implements IAttackDisplay implements IAnimate {
	/**
	 * 原始数据
	 */
	public var originData(get, set):EffectData;

	private var _originData:EffectData;

	private function get_originData():EffectData {
		return _originData;
	}

	private function set_originData(value:EffectData):EffectData {
		_originData = value;
		@:privateAccess for (index => frame in __movieClip.__frames) {
			if (originData != null) {
				var currentFrame = originData.frames[index];
				if (currentFrame != null && currentFrame.sound != null && currentFrame.sound != "") {
					frame.sound = this.role.assets.getSound(currentFrame.sound);
				}
			}
		}
		return value;
	}

	/**
	 * 实例的经过时间
	 */
	public var nowTime:Float = 0;

	/**
	 * 技能特效使用者
	 */
	public var role:GameRole;

	/**
	 * 技能特效发起攻击的方向
	 */
	@:noCompletion private var __hitScaleX:Int = 1;

	public var hitScaleX(get, never):Int;

	private function get_hitScaleX():Int {
		return __hitScaleX;
	}

	/**
	 * 游戏世界
	 */
	public var world:World;

	/**
	 * 攻击的碰撞块预设，如果不存在`hitBody`，则意味着该对象无法被攻击
	 */
	public var hitBody(default, set):Body;

	private function set_hitBody(value:Body):Body {
		if (hitBody != null) {
			hitBody.entity = null;
			if (hitBody.world != null)
				hitBody.world.remove(hitBody);
		}
		this.hitBody = value;
		if (value != null) {
			value.entity = this;
			if (value.world == null && this.world != null) {
				this.world.echoWorld.add(value);
			}
		}
		return body;
	}

	/**
	 * 物理碰撞器
	 */
	public var body(default, set):Body;

	private function set_body(value:Body):Body {
		if (body != null) {
			if (world != null) {
				this.world.skillBoids.remove(body);
			}
			body.entity = null;
			body.on_move = null;
			if (body.world != null)
				body.world.remove(body);
		}
		this.body = value;
		if (value != null) {
			if (world != null) {
				this.world.skillBoids.push(body);
			}
			value.entity = this;
			value.on_move = this.setPoint;
			if (value.world == null && this.world != null) {
				this.world.echoWorld.add(value);
			}
		}
		return body;
	}

	/**
	 * 队伍值
	 */
	public var troop:Int;

	/**
	 * 特效动画显示对象
	 */
	private var __movieClip:MovieClip;

	/**
	 * 跳转帧
	 * @param frame 
	 */
	public function goToFrame(frame:Int):Void {
		if (__movieClip != null) {
			__movieClip.currentFrame = frame;
			__frameDirty = true;
		}
	}

	/**
	 * 特效存活时间，默认为`-1`，当为`-1`时，则会根据特效播放结束后自动消失，设置为`0`则永远播放
	 */
	public var liveTime:Float = -1;

	private var __liveTime:Float = 0.;

	/**
	 * 位移速度
	 */
	public var speed:Point;

	/**
	 * 锁定角色动作，当角色动作变更时，则特效会自动消失
	 */
	public var lockAction:String = null;

	/**
	 * 角色攻击碰撞块有效时间
	 */
	private var __hitCollisionTime:Float = 0;

	/**
	 * 角色攻击碰撞生效帧
	 */
	private var __hitFrameData:FrameData;

	/**
	 * 获得角色攻击碰撞块
	 */
	public var currentHitColliderFrameData(get, set):FrameData;

	/**
	 * 碰撞检测，可以是特效与角色之间的碰撞，也可以是特效与特效之间的碰撞检测
	 * @param collider 
	 * @return Bool
	 */
	public function testCollider(collider:IAttackDisplay):Bool {
		var hitData = this.currentHitColliderFrameData;
		if (hitData != null) {
			var c = this.getColliders();
			for (shape in c) {
				hitData.testCollision(this, shape);
			}
		}
		return false;
	}

	/**
	 * 获得当前碰撞列表
	 * @return Array<Shape>
	 */
	public function getColliders():Array<Shape> {
		var ret = [];
		var hitData = this.currentHitColliderFrameData;
		if (hitData != null && hitData.collision != null) {
			return hitData.collision.shapes;
		}
		return ret;
	}

	public function setPoint(x:Float, y:Float):Void {
		super.x = x;
		super.y = y;
		if (this.hitBody != null) {
			this.hitBody.x = x;
			this.hitBody.y = y;
		}
	}

	override function set_x(value:Float):Float {
		if (this.body != null)
			this.body.x = value;
		else
			super.x = value;
		if (this.hitBody != null)
			this.hitBody.x = value;
		return value;
	}

	override function set_y(value:Float):Float {
		if (this.body != null)
			this.body.y = value;
		else
			super.y = value;
		if (this.hitBody != null)
			this.hitBody.y = value;
		return value;
	}

	/**
	 * 卡帧时间
	 */
	public var cardFrameTime:Float = 0;

	/**
	 * 自定义数据
	 */
	public var customData:Dynamic = {};

	/**
	 * 判断检测碰撞条件
	 * @param body 
	 * @return Bool
	 */
	public function condition(body:Body):Bool {
		return true;
	}

	/**
	 * 碰撞触发
	 * @param body 
	 */
	public function enter(body:Body):Void {}

	/**
	 * 设置卡帧时间
	 * @param time 
	 */
	public function setStopFrameTime(time:Float):Void {
		if (this.role.stoicTime > 0 || this.role.obverseDefanceTime > 0)
			time *= 0.5;
		if (time > this.cardFrameTime) {
			this.cardFrameTime = time;
		}
	}

	private function get_currentHitColliderFrameData():FrameData {
		if (__hitCollisionTime <= 0) {
			return null;
		}
		return this.__hitFrameData;
	}

	private function set_currentHitColliderFrameData(value:FrameData):FrameData {
		this.__hitFrameData = value;
		return value;
	}

	override function __getRect():hx.geom.Rectangle {
		if (__movieClip.data != null) {
			if (__movieClip.data.frameRect != null) {
				__rect.x = __movieClip.data.frameRect.x;
				__rect.y = __movieClip.data.frameRect.y;
				__rect.width = __movieClip.data.frameRect.width;
				__rect.height = __movieClip.data.frameRect.height;
			} else if (__movieClip.data.rect != null) {
				__rect.width = __movieClip.data.rect.width;
				__rect.height = __movieClip.data.rect.height;
			} else {
				__rect.width = __movieClip.data.data.getWidth();
				__rect.height = __movieClip.data.data.getHeight();
			}
		}
		return super.__getRect();
	}

	override function set_blendMode(value:BlendMode):BlendMode {
		switch value {
			case ADD:
				__movieClip.blendMode = ADD;
			case MULTIPLY:
				__movieClip.blendMode = MULTIPLY;
			case NORMAL:
				__movieClip.blendMode = NORMAL;
			case SCREEN:
				__movieClip.blendMode = SCREEN;
			case DIFFERENCE:
				__movieClip.blendMode = DIFFERENCE;
			case SUBTRACT:
				__movieClip.blendMode = SUBTRACT;
			case INVERT:
				__movieClip.blendMode = INVERT;
		}
		return super.set_blendMode(value);
	}

	public var id:String;

	public var display:IAttackDisplay;

	public function new(display:IAttackDisplay, id:String) {
		super();
		this.id = id;
		this.display = display;
		this.role = display.getRole();
		this.__hitScaleX = display.scaleX > 0 ? 1 : -1;
		this.troop = role.troop;
		this.world = role.world;
		this.onCreate();
		this.addChild(__movieClip);
		this.__movieClip.useFrameRect = false;
		this.__movieClip.updateEnabled = false;
		__movieClip.addEventListener(Event.COMPLETE, onPlayComplete);
		__movieClip.addEventListener(Event.CHANGE, onPlayChange);
		__movieClip.play();
		// 不主动触发onUpdate，由GameWorld触发
		this.updateEnabled = false;
		this.mouseChildren = false;
	}

	public function onCreate():Void {
		this.__movieClip = new MovieClip();
		var atlas:XmlAtlas = cast this.role.assets.atlases.get(id);
		if (atlas == null) {
			atlas = cast this.world.assets.atlases.get(id);
		}
		if (atlas != null) {
			for (name in atlas.names) {
				this.__movieClip.addFrame(atlas.bitmapDatas.get(name), 1 / 16);
			}
		}
		if (atlas == null) {
			throw "GameEffect assets not load:" + id;
		}
		var xml:Xml = @:privateAccess atlas.__xml.firstElement();
		// 特效偏移值支持
		var pos = this.role.roleData.getOffestPoint(id);
		if (pos.x != 0 || pos.y != 0) {
			this.originX = pos.x;
			this.originY = pos.y;
		} else if (xml.exists("px") && xml.exists("py")) {
			this.originX = Std.parseFloat(xml.get("px"));
			this.originY = Std.parseFloat(xml.get("py"));
		}

		// 测试代码
		// var body = new Body({
		// 	x: this.x,
		// 	y: this.y,
		// 	shape: {
		// 		type: POLYGON,
		// 		vertices: [new Vector2(-50, -50), new Vector2(50, -50), new Vector2(50, 0), new Vector2(-50, 0)]
		// 	}
		// });
		// this.body = body;
		// this.world.skillBoids.push(body);
	}

	override private function get_originX():Float {
		return __movieClip.originX;
	}

	override function set_originX(value:Float):Float {
		__movieClip.originX = value;
		return value;
	}

	override private function get_originY():Float {
		return __movieClip.originY;
	}

	override function set_originY(value:Float):Float {
		__movieClip.originY = value;
		return value;
	}

	public var currentFrameData(get, never):FrameData;

	private function get_currentFrameData():FrameData {
		if (this.originData == null)
			return null;
		return this.originData.frames[this.__movieClip.currentFrame];
	}

	/**
	 * 计算缩放、旋转、透明度等实现
	 * @param dt 
	 */
	public function onTransform(dt:Float):Void {
		if (this.originData == null)
			return;
		var frameData = this.currentFrameData;
		var data = this.__movieClip.currentCustomData;
		if (data != null)
			ITimelineTools.updateTransform(this.__movieClip, frameData, this.originData, data.x, data.y, 1, dt);
		else {
			ITimelineTools.updateTransform(this.__movieClip, frameData, this.originData, 0, 0, 1, dt);
		}
	}

	private var __updateDt:Float = 0;

	override function onUpdate(dt:Float) {
		super.onUpdate(dt);

		if (this.hasEventListener(RoleEvent.FRAME_ENTER))
			this.dispatchEvent(new RoleEvent(RoleEvent.FRAME_ENTER));

		if (__hitFrameData != null && __hitFrameData.hitData.liveTime > 0) {
			__hitFrameData.hitData.liveTime -= dt;
			if (__hitFrameData.hitData.liveTime <= 0) {
				__hitFrameData = null;
			}
		}

		if (this.cardFrameTime > 0) {
			this.cardFrameTime -= dt;
			return;
		}

		nowTime += dt;

		__updateDt += dt;

		while (__updateDt > GameOption.DT_FPS) {
			__updateDt -= GameOption.DT_FPS;
			this.__advance(GameOption.DT_FPS);
		}

		this.__advance(0);

		if (this.hasEventListener(RoleEvent.FRAME_EXIT))
			this.dispatchEvent(new RoleEvent(RoleEvent.FRAME_EXIT));
	}

	private function __advance(dt:Float):Void {
		this.__movieClip.onUpdate(dt);
		this.onTransform(dt);
		if (liveTime > 0) {
			__liveTime += dt;
			if (__liveTime > liveTime) {
				this.clean();
			}
		}
		if (this.world == null)
			return;
		if (followOffsetPoint != null && (lockAction == null || this.role.actionName == this.lockAction)) {
			if (this.followTarget != null) {
				this.y = this.followTarget.y + followOffsetPoint.y * this.display.scaleY;
				this.x = this.followTarget.x + followOffsetPoint.x * this.display.scaleX;
			} else {
				this.x = this.display.x + followOffsetPoint.x * this.display.scaleX;
				this.y = this.display.y + followOffsetPoint.y;
			}
			if (originData != null)
				this.scaleX = this.display.scaleX * originData.scaleX;
		} else if (speed != null && (speed.x != 0 || speed.y != 0)) {
			if (this.body != null) {
				this.body.velocity.x = speed.x / GameOption.DT_FPS * (this.scaleX > 0 ? 1 : -1);
				this.body.velocity.y = speed.y / GameOption.DT_FPS;
			} else {
				this.x += speed.x * dt / GameOption.DT_FPS * (this.scaleX > 0 ? 1 : -1);
				this.y += speed.y * dt / GameOption.DT_FPS;
			}
		}
		if (this.originData != null && this.originData.frames.length > 0) {
			// 根据帧数据进行播放位移数据，如果存在的话
			ITimelineTools.updateMove(this, currentFrameData, this.originData, 1, dt);
			if (__frameDirty) {
				// 特效解析处理
				ITimelineTools.parserGameEffect(currentFrameData, this);
				// 碰撞块检测
				if (currentFrameData != null && currentFrameData.collision != null && currentFrameData.collision.shapes.length > 0) {
					if (currentFrameData.shareCollision) {
						if (__hitFrameData != null)
							__hitFrameData.collision = currentFrameData.collision;
					} else {
						__hitFrameData = currentFrameData.copy();
						__hitCollisionTime = currentFrameData.collisionLiveTime;
						__hitFrameData.hitData.liveTime = __hitFrameData.collisionLiveTime;
						__hitFrameData.hitData.updateCreatePoint(this.x, this.y, this.scaleX > 0 ? 1 : -1);
						__hitFrameData.hitData.hitedTime = 0;
					}
					currentFrameData.collision.x = this.x;
					currentFrameData.collision.y = this.y;
					currentFrameData.collision.scale_x = this.scaleX;
					currentFrameData.collision.scale_y = this.scaleY;
				}
				__frameDirty = false;
			}
		}
		if (lockAction != null && lockAction != this.role.currentActionData.name) {
			this.alpha -= 0.05;
			if (this.alpha <= 0.) {
				this.clean();
			}
		}
	}

	/**
	 * 根据昵称获得特效显示对象（当前角色或者特效释放的特效），但请注意，自已释放出去的特效中产生的特效，此方法是无法定位的
	 * @param name 
	 * @return DisplayObject
	 */
	public function getGameEffectByName(name:String):GameEffect {
		if (world != null) {
			return cast(world, GameWorld).getGameEffectByName(name, this);
		}
		return null;
	}

	public function clean():Void {
		this.onClean();
	}

	override function onRemoveToStage() {
		super.onRemoveToStage();
	}

	/**
	 * 遮罩技能使用的矩形
	 */
	private var maskSkillRect:Rectangle = new Rectangle();

	private var __frameDirty:Bool = true;

	private function onPlayChange(e:Event):Void {
		if (this.__movieClip.data != null) {
			this.setTransformDirty();
		}
		__frameDirty = true;
		if (this.hasEventListener(RoleEvent.FRAME_CHANGE))
			this.dispatchEvent(new RoleEvent(RoleEvent.FRAME_CHANGE));
	}

	override function dispatchEvent(event:Event) {
		if (event is RoleEvent) {
			var roleEvent:RoleEvent = cast event;
			roleEvent.action = null;
			roleEvent.frame = this.currentFrame;
		}
		super.dispatchEvent(event);
	}

	private function onPlayComplete(e:Event):Void {
		if (this.liveTime == -1)
			this.clean();
	}

	/**
	 * 跟随角色的位置
	 */
	public var followOffsetPoint:Point;

	/**
	 * 跟随目标
	 */
	public var followTarget:DisplayObject;

	/**
	 * 是否击中敌人就消失
	 */
	public var isHitClean:Bool = false;

	/**
	 * 角色脚本
	 */
	public var script:GameEffectScript;

	/**
	 * 设置脚本源类名
	 */
	public var scriptClass(default, set):String;

	private function set_scriptClass(value:String):String {
		this.scriptClass = value;
		var source = this.role.assets.strings.get(value);
		if (source != null)
			this.script = new GameEffectScript(this, value, source);
		return value;
	}

	/**
	 * 帧率设置
	 */
	public var fps(default, set):Float;

	private function set_fps(value:Float):Float {
		this.fps = value;
		var fpsDt = 1 / value;
		@:privateAccess for (frame in __movieClip.__frames) {
			frame.duration = fpsDt;
		}
		return value;
	}

	public function applyEffectData(data:EffectData, x:Float, y:Float, diretion:Int):Void {
		__movieClip.blendMode = data.blendMode;
		__movieClip.currentFrame = data.startFrame;
		this.x = x + data.x * role.assets.data.attrData.getBodyScale() * diretion;
		this.y = y + data.y * role.assets.data.attrData.getBodyScale();
		this.scaleX = data.scaleX * role.assets.data.attrData.getBodyScale() * diretion;
		this.scaleY = data.scaleY * role.assets.data.attrData.getBodyScale();
		this.rotation = data.rotation * diretion;
		this.alpha = data.alpha;
		this.isHitClean = data.isHitClean;
		// 弃用，性能不好，不采取此改色方案
		// if (data.colorMatrixFilter != null) {
		// var matrix = new ColorMatrixFilter(data.colorMatrixFilter);
		// bitmap.filters = [matrix];
		// }
		if (data.isApplyColorTranform) {
			__movieClip.colorTransform = new ColorTransform(data.R, data.G, data.B, 1, data.ROffset, data.GOffset, data.BOffset, 0);
		}
		this.blendMode = data.blendMode;
		this.liveTime = data.liveTime;
		if (data.speed != null) {
			this.speed = data.speed.clone();
		}
		this.name = data.displayName;
		if (data.isLockAction) {
			lockAction = this.role.currentActionData.name;
		}

		this.fps = data.fps;

		if (data.isFollow) {
			followOffsetPoint = new Point(data.x * role.assets.data.attrData.getBodyScale(), data.y * role.assets.data.attrData.getBodyScale());
		}
		if (data.script != null) {
			// 查询脚本
			this.scriptClass = data.script;
		}
	}

	public var currentFrame(get, set):Int;

	private function get_currentFrame():Int {
		return __movieClip.currentFrame;
	}

	private function set_currentFrame(value:Int):Int {
		__movieClip.currentFrame = value;
		return value;
	}

	public function nextFrame():Void {
		if (__movieClip.currentFrame < __movieClip.totalFrame - 1)
			__movieClip.currentFrame++;
	}

	public function prevFrame():Void {
		if (__movieClip.currentFrame > 0)
			__movieClip.currentFrame--;
	}

	/**
	 * 获得角色，如果角色自身存在，则返回角色自身，否则返回关系role
	 * @return Role
	 */
	public function getRole():GameRole {
		return role;
	}

	/**
	 * 成功攻击敌人时触发
	 * @param enemy 
	 */
	public function onAttackEnemy(enemy:IAttackDisplay):Void {
		if (this.isHitClean) {
			this.clean();
		}
	}

	public function onClean():Void {
		if (this.world != null) {
			this.world.removeDisplay(this);
		}
		this.body = null;
		this.hitBody = null;
		this.world = null;
	}
}
